package com.px.project.module.ht.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import com.px.common.config.exception.ServiceException;
import com.px.common.config.security.LoginContextHolder;
import com.px.common.config.security.SysLoginUser;
import com.px.common.dto.BaseQueryDto;
import com.px.common.enums.ApprovePermEnum;
import com.px.common.enums.ApproveStatusEnum;
import com.px.common.enums.ProcessEnum;
import com.px.common.enums.TaskApproveCategoryEnum;
import com.px.common.service.ActivitiService;
import com.px.common.service.SysUserService;
import com.px.common.util.CommonUtil;
import com.px.common.util.DictUtil;
import com.px.common.vo.HistoricTaskVo;
import com.px.project.module.ht.dto.ApproveDto;
import com.px.project.module.ht.dto.MonthPlanDto;
import com.px.project.module.ht.dto.MonthPlanQueryDto;
import com.px.project.module.ht.entity.MonthPlan;
import com.px.project.module.ht.entity.MonthPlanDetail;
import com.px.project.module.ht.mapper.MonthPlanMapper;
import com.px.project.module.ht.service.MonthPlanDetailService;
import com.px.project.module.ht.service.MonthPlanService;
import com.px.project.module.ht.vo.MonthPlanVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 品讯科技
 * @since 2023-09-26
 */
@Service
@RequiredArgsConstructor
public class MonthPlanServiceImpl extends ServiceImpl<MonthPlanMapper, MonthPlan> implements MonthPlanService {
    private static final Integer INDEX_ZERO = 0;

    private static final String SUBJECT_FIRST_LEVEL_APPROVAL = "待一级审批";

    private static final String SUBJECT_SECONDARY_APPROVAL = "待二级审批";

    private static final String SUBJECT_THIRD_LEVEL_APPROVAL = "待三级审批";

    private final MonthPlanMapper monthPlanMapper;

    private final ActivitiService activitiService;

    private final MonthPlanDetailService monthPlanDetailService;

    private final SysUserService sysUserService;

    @Override
    public Page<MonthPlan> findPages(BaseQueryDto<MonthPlan> baseQuery, MonthPlanQueryDto monthPlanQuery) {
        Page<MonthPlan> pages = this.monthPlanMapper.selectPages(baseQuery.toPage(), monthPlanQuery);
        List<String> processInstanceIds =
                pages.getRecords().stream().map(MonthPlan::getProcInstId).collect(Collectors.toList());
        List<Task> tasks = this.activitiService.findBatchTasks(processInstanceIds);
        pages.getRecords().forEach(monthPlan -> this.writeBackMonthPlan(tasks, monthPlan));
        return pages;
    }

    private void writeBackMonthPlan(List<Task> tasks, MonthPlan monthPlan) {
        monthPlan.setApproveStatusName(ApproveStatusEnum.getNameByCode(monthPlan.getApproveStatus()).orElse(null));
        List<Task> taskList =
            tasks.stream().filter(task -> CharSequenceUtil.equals(task.getProcessInstanceId(), monthPlan.getProcInstId()))
                .collect(Collectors.toList());
        if (ObjectUtil.isEmpty(taskList)) {
            return;
        }
        Task task = taskList.get(INDEX_ZERO);
        if (CharSequenceUtil.equals(ApprovePermEnum.FIRST.getCode(), task.getTaskDefinitionKey())) {
            monthPlan.setApproveStatusName(SUBJECT_FIRST_LEVEL_APPROVAL);
        } else if (CharSequenceUtil.equals(ApprovePermEnum.SECOND.getCode(), task.getTaskDefinitionKey())) {
            monthPlan.setApproveStatusName(SUBJECT_SECONDARY_APPROVAL);
        } else if (CharSequenceUtil.equals(ApprovePermEnum.THIRD.getCode(), task.getTaskDefinitionKey())) {
            monthPlan.setApproveStatusName(SUBJECT_THIRD_LEVEL_APPROVAL);
        }
        Boolean isPermission = this.activitiService.checkIsPermission(monthPlan.getProcInstId(),
                LoginContextHolder.me().getUsername());
        monthPlan.setCanApprove(Boolean.TRUE.compareTo(isPermission));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAndSubmit(MonthPlanDto param) {
        List<MonthPlanDetail> detailList = new ArrayList<>();
        Set<String> set = new TreeSet<>();
        param.getDetails().forEach(detail -> {
            CommonUtil.setNullValue(detail);
            MonthPlanDetail monthPlanDetail = new MonthPlanDetail();
            BeanUtils.copyProperties(detail, monthPlanDetail);
            set.add(DateUtil.format(detail.getWorkDateStart(), "yyyy-MM-dd"));
            set.add(DateUtil.format(detail.getWorkDateEnd(), "yyyy-MM-dd"));
            detailList.add(monthPlanDetail);
        });
        String first = null;
        String last = null;
        int i = 0;
        for (String str : set) {
            if (i == 0) {
                first = str;
            }
            last = str;
            i++;
        }
        String monthRange = first + "~" + last;
        SysLoginUser loginUser = LoginContextHolder.me();
        MonthPlan monthPlan = MonthPlan.builder().approveStatus(ApproveStatusEnum.APPROVING.getCode())
            .applyUserId(loginUser.getId()).applyTime(new Date()).phone(loginUser.getPhone()).monthRange(monthRange)
            .monthDateStart(DateUtil.parse(first, "yyyy-MM-dd")).monthDateEnd(DateUtil.parse(last, "yyyy-MM-dd"))
            .applyOrgId(loginUser.getCompanyId()).applyOrgName(loginUser.getCompanyName()).build();
        save(monthPlan);
        ProcessInstance processInstance = activitiService.startProcess(ProcessEnum.MONTH_PLAN.getCode(),
            String.valueOf(monthPlan.getId()), new HashMap<>());
        monthPlan.setProcInstId(processInstance.getProcessInstanceId());
        monthPlan.setNextApproveNode(activitiService.getFlowNexApproveNode(monthPlan.getProcInstId()));
        updateById(monthPlan);
        detailList.forEach(detail -> detail.setMonthPlanId(monthPlan.getId()));
        monthPlanDetailService.saveBatch(detailList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean approve(ApproveDto approve) {
        this.activitiService.isFinished(approve.getProcInstId());
        this.activitiService.handleProcess(approve.getProcInstId(), approve.getApproveOperation(),
                approve.getComment(), LoginContextHolder.me().getUsername());
        /* 更新业务表至下一个审批节点 */
        super.update(new LambdaUpdateWrapper<MonthPlan>()
            .set(MonthPlan::getNextApproveNode, this.activitiService.getFlowNexApproveNode(approve.getProcInstId()))
            .eq(MonthPlan::getProcInstId, approve.getProcInstId()));
        return Boolean.TRUE;
    }

    @Override
    public MonthPlanVo findInfoOne(Long id) {
        MonthPlan plan = super.getById(id);
        MonthPlanVo monthPlan = new MonthPlanVo();
        BeanUtils.copyProperties(plan, monthPlan);
        monthPlan.setApplyUserName(this.sysUserService.getById(monthPlan.getApplyUserId()).getName());
        monthPlan.setApproveStatusName(ApproveStatusEnum.getNameByCode(monthPlan.getApproveStatus()).orElse(null));
        this.writeBackDoingMonthPlan(monthPlan);
        this.writeBackHistoricMonthPlan(monthPlan);
        this.writeBackMonthPlanDetail(monthPlan);
        return monthPlan;
    }

    private void writeBackDoingMonthPlan(MonthPlanVo monthPlan) {
        if (ObjectUtil.notEqual(ApproveStatusEnum.APPROVING.getCode(), monthPlan.getApproveStatus())) {
            return;
        }
        Optional<Task> taskOptional = this.activitiService.findTask(monthPlan.getProcInstId());
        if (taskOptional.isPresent()) {
            Task task = taskOptional.get();
            if (CharSequenceUtil.equals(ApprovePermEnum.FIRST.getCode(), task.getTaskDefinitionKey())) {
                monthPlan.setApproveStatusName(SUBJECT_FIRST_LEVEL_APPROVAL);
            } else if (CharSequenceUtil.equals(ApprovePermEnum.SECOND.getCode(), task.getTaskDefinitionKey())) {
                monthPlan.setApproveStatusName(SUBJECT_SECONDARY_APPROVAL);
            } else if (CharSequenceUtil.equals(ApprovePermEnum.THIRD.getCode(), task.getTaskDefinitionKey())) {
                monthPlan.setApproveStatusName(SUBJECT_THIRD_LEVEL_APPROVAL);
            }
            Boolean isPermission = this.activitiService.checkIsPermission(monthPlan.getProcInstId(),
                    LoginContextHolder.me().getUsername());
            monthPlan.setCanApprove(Boolean.TRUE.compareTo(isPermission));
        }
    }

    private void writeBackHistoricMonthPlan(MonthPlanVo monthPlan) {
        List<HistoricTaskVo> historicTaskVoList = this.activitiService.getHistoricTaskList(monthPlan.getProcInstId());
        for (HistoricTaskVo historicTaskVo : historicTaskVoList) {
            if (ObjectUtil.notEqual(historicTaskVo.getCategory(), TaskApproveCategoryEnum.BACK.getValue())) {
                if (ObjectUtil.equal(ApprovePermEnum.FIRST.getCode(), historicTaskVo.getTaskDefinitionKey())) {
                    monthPlan.setFirstApproveTime(historicTaskVo.getEndTime());
                } else if (ObjectUtil.equal(ApprovePermEnum.SECOND.getCode(), historicTaskVo.getTaskDefinitionKey())) {
                    monthPlan.setSecondApproveTime(historicTaskVo.getEndTime());
                } else if (ObjectUtil.equal(ApprovePermEnum.THIRD.getCode(), historicTaskVo.getTaskDefinitionKey())) {
                    monthPlan.setThirdApproveTime(historicTaskVo.getEndTime());
                }
            }
        }
    }

    private void writeBackMonthPlanDetail(MonthPlanVo monthPlan) {
        List<MonthPlanDetail> monthPlanDetailList =
            this.monthPlanDetailService.list(new LambdaQueryWrapper<MonthPlanDetail>()
                .eq(MonthPlanDetail::getMonthPlanId, monthPlan.getId()).orderByAsc(MonthPlanDetail::getCreateTime));
        monthPlanDetailList.forEach(monthPlanDetail -> {
            StringBuilder sb = new StringBuilder();
            sb.append(monthPlanDetail.getWorkTimeStart());
            if (monthPlanDetail.getWorkTimeEnd().isBefore(monthPlanDetail.getWorkTimeStart())) {
                sb.append("至");
            } else {
                sb.append("至次日");
            }
            sb.append(monthPlanDetail.getWorkTimeEnd());
            monthPlanDetail.setWorkTimeStr(sb.toString());
            monthPlanDetail.setWorkLineTypeName(
                DictUtil.getTypeName("ht_line_type", monthPlanDetail.getWorkLineType().toString()));
        });
        monthPlan.setMonthPlanDetailList(monthPlanDetailList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchDelete(List<Long> idList) {
        for (Long id : idList) {
            MonthPlan plan = getById(id);
            if (Objects.isNull(plan)) {
                continue;
            }
            if (ObjectUtil.equal(ApproveStatusEnum.APPROVED.getCode(), plan.getApproveStatus())) {
                throw new ServiceException("流程审批已完成，无法删除");
            }
            this.activitiService.isFinished(plan.getProcInstId());
            activitiService.deleteProcess(plan.getProcInstId());
        }
        removeByIds(idList);
        return true;
    }
}